﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using COFE.Core.Profile;
using COFE.Core.Lister;
using NUnit.Framework;
using COFE.Core.Utils;
using COFE.Core;

namespace COFE.Data.Lister
{
    public class DBVolumeRegistrar : IVolumeRegistrar
    {
        #region Constructor 

        public DBVolumeRegistrar()
        {
            _cachedVolumes = new Dictionary<string, IVolume>();
        }

        #endregion

        #region Methods

        public static void RegisterVolumeFactory(IFactory<IDirectoryLister> listerFactory)
        {
            Assert.IsNotNull(listerFactory);
            Assert.IsNotNullOrEmpty(listerFactory.FactoryKey);

            //lock (_registeredFactories)
            //    if (!_registeredFactories.ContainsKey(listerFactory.FactoryKey))
            //        _registeredFactories.Add(listerFactory.FactoryKey, listerFactory);
            FactoryRegister<IDirectoryLister>.RegisterFactory(listerFactory.FactoryKey, listerFactory);
        }

        public void RegisterFactory(IFactory<IDirectoryLister> listerFactory)
        {
            RegisterVolumeFactory(listerFactory);
        }

        private Dictionary<string, IVolume> getRegisteredVolumes()
        {
            return COFEDBShared.DBOperations.ReadDatabase((context) =>
            {
                Func<COFE.Data.Volume, COFE.Core.Profile.IVolume> createVolume =
                    (v) =>
                    {
                        //return new COFE.Core.Profile.Volume(v.FactoryKey, v.VolumeId,
                        //    (VolumeAccessMode)v.AccessMode,
                        //    v.Parameters);
                        return (IVolume)ConstructVolume(v.FactoryKey, v.VolumeId, v.Parameters,
                            (VolumeAccessMode)v.AccessMode);
                    };

                Dictionary<string, IVolume> retVal = new Dictionary<string, IVolume>();
                foreach (var vol in context.Volumes)
                {
                    try
                    {
                        if (!(_errorVolumes.Contains(vol.VolumeId)))
                            retVal.Add(vol.VolumeId,
                                cache(vol.VolumeId, () => createVolume(vol)));
                    }
                    catch (Exception ex)
                    {
                        _errorVolumes.Add(vol.VolumeId);
                        Console.WriteLine(String.Format("DBVolumeRegistrar: Vol={0} Err={1}", vol.VolumeId, ex.Message));
                    }
                }
                return retVal;
            });
        }


        public IVolume ConstructVolume(string factoryKey, IDirectoryLister lister, VolumeAccessMode defAccessMode)
        {
            if (lister is PrimaryDirectoryListerBase<COFE.Data.Entry>)
                return new COFE.Core.Profile.Volume(factoryKey, lister, lister as IPrimaryDirectoryLister, defAccessMode);

            if (lister is IPrimaryDirectoryLister)
                //Primary volume.
                return new COFE.Core.Profile.Volume(factoryKey, lister, new DBPrimaryDirectoryLister(lister as IPrimaryDirectoryLister), defAccessMode);
            //Secondary
            else return new COFE.Core.Profile.Volume(factoryKey, lister, new DBSecondaryDirectoryLister(lister as ISecondaryDirectoryLister), defAccessMode);

        }

        public IVolume ConstructVolume(string factoryKey, string listerKey, string paramString, VolumeAccessMode defAccessMode)
        {
            Assert.IsNotNullOrEmpty(factoryKey);
            
            IFactory<IDirectoryLister> factory;
            factory = FactoryRegister<IDirectoryLister>.FindFactory(factoryKey);
            if (factory == null)
                throw new KeyNotFoundException("Factory " + factoryKey + " is not found.");

            IDirectoryLister lister;

            if (factoryKey == ProfileRootDirectoryLister.ProfileRootFactoryKey)
                lister = new ProfileRootDirectoryLister(factory, listerKey);
            else
            {
                Assert.IsNotNullOrEmpty(listerKey);
                lister = factory.Construct(listerKey, paramString);
            }
            IVolume volume = ConstructVolume(factoryKey, lister, defAccessMode);
            return volume;
        }

        public void RegisterVolume(IVolume volume)
        {
            if (volume.FactoryKey == null)
                return;

            COFEDBShared.DBOperations.UpdateDatabase((context) =>
            {
                var vol = context.Volumes.FirstOrDefault(v => v.VolumeId == volume.ListerKey);

                if (vol == null)
                {
                    Volume newVol;
                    Assert.IsNotNullOrEmpty(volume.ListerKey);
                    if (volume.IsPrimaryDirectoryLister())
                        newVol =
                            PrimaryVolume.CreatePrimaryVolume(volume.ListerKey, volume.FactoryKey,
                            volume.GetArgs(), (int)volume.AccessMode);
                    else newVol =
                            SecondaryVolume.CreateSecondaryVolume(volume.ListerKey, volume.FactoryKey,
                            volume.GetArgs(), (int)volume.AccessMode);

                    context.Volumes.AddObject(newVol);
                }
                else
                {
                    vol.AccessMode = (int)volume.AccessMode;
                    vol.Parameters = volume.GetArgs();
                }


                context.SaveChanges();
            });

            removeCache(volume.ListerKey);
        }


        public void RemoveVolume(string volumeId)
        {
            COFEDBShared.DBOperations.UpdateDatabase((context) =>
            {
                var delVolume = (from v in context.Volumes where v.VolumeId == volumeId select v).FirstOrDefault();
                context.Volumes.DeleteObject(delVolume);
                context.SaveChanges();
            });

            removeCache(volumeId);
        }

        public IVolume LookupRegisterVolume(string volumeId)
        {
            return cache(volumeId, () => COFEDBShared.DBOperations.ReadDatabase((context) =>
            {
                //Console.WriteLine(volumeId);
                var foundVolume = (from v in context.Volumes where v.VolumeId == volumeId select v).FirstOrDefault();
                if (foundVolume == null)
                    return null;
                return ConstructVolume(foundVolume.FactoryKey, foundVolume.VolumeId,
                    foundVolume.Parameters,
                    (VolumeAccessMode)foundVolume.AccessMode);
            }));
        }

        public void removeCache(string listerKey)
        {
            lock (_cachedVolumes)
                if (_cachedVolumes.ContainsKey(listerKey))
                    _cachedVolumes.Remove(listerKey);
        }

        private IVolume cache(string listerKey, Func<IVolume> volumeFunc)
        {
            lock (_cachedVolumes)
            {
                if (!(_cachedVolumes.ContainsKey(listerKey)))
                {
                    var volumeCache = volumeFunc();
                    if (volumeCache != null)
                        _cachedVolumes.Add(listerKey, volumeCache);
                    else return null;
                }

                return _cachedVolumes[listerKey];
            }
        }

        #endregion

        #region Data

        private List<string> _errorVolumes = new List<string>();
        private Dictionary<string, IVolume> _cachedVolumes;

        #endregion

        #region Public Properties

        public Dictionary<string, IVolume> Volumes
        {
            get { return getRegisteredVolumes(); }
        }

        public Dictionary<string, IFactory<IDirectoryLister>> Factories
        { get { return FactoryRegister<IDirectoryLister>.Factories; } }

        #endregion







    }
}
